package com.juyu.miliao.api.service;

import com.juyu.miliao.api.dao.mysql.BillDetailDao;
import com.juyu.miliao.api.dao.redis.SignInRedisDao;
import com.juyu.miliao.api.domain.SignIn;
import com.juyu.miliao.api.domain.User;
import com.juyu.miliao.api.domain.vo.SignInVo;
import com.juyu.miliao.api.exception.NotVipException;
import com.juyu.miliao.api.exception.SignInException;
import com.juyu.miliao.api.exception.VipSignInRewardException;
import com.juyu.miliao.common.constant.BeansType;
import com.juyu.miliao.common.domain.BillDetailBeans;
import com.juyu.miliao.common.util.SegmentedLock;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.stream.IntStream;

import static java.util.stream.Collectors.toList;

/**
 * @ClassName: SignInService
 * @Description: 签到有礼
 * @Author zhangcheng
 * @Date 19-9-11下午3:48
 */
@Service
public class SignInService {
    /**
     * 签到奖励
     */
    private final Long[] signInRewardArr = {1000L, 2000L, 3000L, 4000L, 5000L, 7000L, 10000L};
    /**
     * vip奖励
     */
    private final Long[] vipRewardArr = {5000L, 10000L, 20000L, 40000L, 80000L};
    /**
     * 分段锁
     */
    private final SegmentedLock segmentedLock = new SegmentedLock();
    @Resource
    private SignInRedisDao signInRedisDao;
    @Resource
    private UserWalletService userWalletService;
    @Resource
    private BillDetailDao billDetailDao;
    @Resource
    private UserInfoService userInfoService;
    @Resource
    private GameDataService gameDataService;

    /**
     * 获取用户的签到数据
     *
     * @param userId
     * @return
     */
    public SignIn getSignInData(Long userId) {
        Lock lock = segmentedLock.getLock(String.valueOf(userId));
        lock.lock();
        try {
            SignIn signIn = signInRedisDao.getSignInData(userId)
                    .orElseGet(() -> {
                        signInRedisDao.initSignInData(userId);
                        SignIn s = new SignIn();
                        s.setConsecutiveDays(0);
                        s.setVipConsecutiveDays(0);
                        s.setSigned(false);
                        List<Integer> vipRewardList = IntStream.range(0, 5)
                                .map(num -> 0)
                                .boxed()
                                .collect(toList());
                        s.setVipRewardList(vipRewardList);
                        return s;
                    });
            return signIn;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 签到
     *
     * @param userId
     */
    @Transactional(rollbackFor = Exception.class)
    public SignInVo signIn(Long userId) {
        LocalDate date = LocalDate.now();
        User user = userInfoService.getUserInfoById((int) (long) userId);
        Lock lock = segmentedLock.getLock(String.valueOf(userId));
        lock.lock();
        try {
            SignIn signIn = getSignInData(userId);
            if (signIn.getSigned()) {
                throw new SignInException();
            }
            int consecutiveDays = signIn.getConsecutiveDays();
            int vipConsecutiveDays = signIn.getVipConsecutiveDays();
            if (consecutiveDays < 7) {
                //更新签到天数
                consecutiveDays += 1;
                signInRedisDao.updateConsecutiveDays(userId);
                if (vipConsecutiveDays < 30) {
                    signInRedisDao.updateVipConsecutiveDays(userId);
                    vipConsecutiveDays += 1;
                }
                //更新用户钱包，添加明细
                long beans = signInRewardArr[consecutiveDays - 1];
                if (user.getIsPlatinumVip() == 1) {
                    beans *= 2;
                }
                userWalletService.addBeans(userId, beans);
                BillDetailBeans billDetailBeans = new BillDetailBeans();
                billDetailBeans.setBeansType(BeansType.SIGN_IN_REWARD.getCode());
                billDetailBeans.setBeans(beans);
                billDetailBeans.setRatio(0);
                billDetailBeans.setUserId(userId);
                billDetailDao.insertBillDetailBeans(billDetailBeans);
                //更新vip奖励
                if (user.getIsVip() == 1 || user.getIsSvip() == 1 || user.getIsPlatinumVip() == 1) {
                    switch (vipConsecutiveDays) {
                        case 3:
                            if (signIn.getVipRewardList().get(0) == 0) {
                                signInRedisDao.updateVipReward(userId, 0L);
                            }
                            break;
                        case 7:
                            if (signIn.getVipRewardList().get(1) == 0) {
                                signInRedisDao.updateVipReward(userId, 1L);
                            }
                            break;
                        case 14:
                            if (signIn.getVipRewardList().get(2) == 0) {
                                signInRedisDao.updateVipReward(userId, 2L);
                            }
                            break;
                        case 21:
                            if (signIn.getVipRewardList().get(3) == 0) {
                                signInRedisDao.updateVipReward(userId, 3L);
                            }
                            break;
                        case 30:
                            if (signIn.getVipRewardList().get(4) == 0) {
                                signInRedisDao.updateVipReward(userId, 4L);
                            }
                            break;
                        default:
                    }
                }
                //更新游戏数据
                gameDataService.increaseSignInUserCount(date, userId);
                gameDataService.increaseSignInAwardBeans(date, beans);
                //返回处理结果
                SignInVo signInVo = new SignInVo();
                signInVo.setBeans(beans);
                signInVo.setPlatinumVip(user.getIsPlatinumVip() == 1);
                return signInVo;
            } else {
                throw new SignInException();
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * 领取vip奖励
     *
     * @param userId
     * @param vipRewardId
     */
    public void award(Long userId, Long vipRewardId) {
        User user = userInfoService.getUserInfoById((int) (long) userId);
        if (user.getIsVip() != 1 && user.getIsSvip() != 1 && user.getIsPlatinumVip() != 1) {
            throw new NotVipException();
        }
        LocalDate date = LocalDate.now();
        Lock lock = segmentedLock.getLock(String.valueOf(userId));
        lock.lock();
        try {
            SignIn signIn = getSignInData(userId);
            int status = signIn.getVipRewardList().get((int) (long) vipRewardId);
            if (status == 1) {
                //更新领取状态
                signIn.getVipRewardList().set((int) (long) vipRewardId, 2);
                signInRedisDao.updateVipReward(userId, vipRewardId);
                //更新用户钱包，添加明细
                long beans = vipRewardArr[(int) (long) vipRewardId];
                userWalletService.addBeans(userId, beans);
                BillDetailBeans billDetailBeans = new BillDetailBeans();
                billDetailBeans.setBeansType(BeansType.VIP_SIGN_IN_REWARD.getCode());
                billDetailBeans.setBeans(beans);
                billDetailBeans.setRatio(0);
                billDetailBeans.setUserId(userId);
                billDetailDao.insertBillDetailBeans(billDetailBeans);
                //如果全部领取则重置
                long awardCount = signIn.getVipRewardList().stream().filter(s -> s == 2).count();
                if (awardCount == 5) {
                    signInRedisDao.resetVipConsecutiveDays(userId);
                }
                //更新游戏数据
                gameDataService.increaseVipSignInUserCount(date, userId);
                gameDataService.increaseVipSignInAwardBeans(date, beans);
            } else {
                throw new VipSignInRewardException();
            }
        } finally {
            lock.unlock();
        }
    }

}
